package com.example.config;

import com.example.entity.RestBean;
import com.example.entity.dto.auth.Account;
import com.example.entity.vo.response.auth.AuthorizeVO;
import com.example.filter.JwtAuthorizeFilter;
import com.example.service.AccountService;
import com.example.utils.JwtUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.io.PrintWriter;

/**
 * SpringSecurity配置类，用于配置安全过滤连，认证处理逻辑、异常处理等
 *
 * @auth shanyue
 * @date 2025年06月16日8:29
 */
@Configuration
public class SecurityConfiguration {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private JwtAuthorizeFilter jwtAuthorizeFilter;

    @Resource
    private AccountService accountService;

    /**
     * 配置SpringSecurity安全过滤器链
     *
     * @param httpSecurity 安全配置对象
     * @return 过滤器链对象
     * @throws Exception 配置异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                //配置接口权限规则
                .authorizeHttpRequests(conf -> conf
                        .requestMatchers("/api/auth/**", "/error").permitAll()//对验证、错误的接口放行
                        .anyRequest().authenticated()//其他所有接口需要验证
                )
                //配置表单登录
                .formLogin(conf -> conf
                        .loginProcessingUrl("/api/auth/login")//登录接口
                        .successHandler(this::onAuthenticationSuccess)//成功情况
                        .failureHandler(this::onAuthenticationFailure)//失败情况
                )
                //配置注销接口
                .logout(conf -> conf
                        .logoutUrl("/api/auth/logout")//登出接口
                        .logoutSuccessHandler(this::onLogoutSuccess)//成功情况
                )
                //配置异常处理
                .exceptionHandling(conf -> conf
                        .authenticationEntryPoint(this::onUnauthorized)
                        .accessDeniedHandler(this::onAccessDeny)
                )
                //关闭CSRF
                .csrf(AbstractHttpConfigurer::disable)//关闭csrf
                //配置Session管理为无状态
                .sessionManagement(conf -> conf
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)//session修改为无状态
                )
                //在用户名密码认证前执行JWT过滤器
                .addFilterBefore(jwtAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    /**
     * 登录成功处理方法，返回JWT令牌及相关信息
     *
     * @param request        请求对象
     * @param response       响应对象
     * @param authentication 认证对象，包含用户信息
     * @throws IOException IO异常
     */
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        //获取认证的用户对象
        User user = (User) authentication.getPrincipal();
        //获取用户信息
        Account account = accountService.findAccountByNameOrEmail(user.getUsername());
        //创建JWT令牌
        String token = jwtUtil.createJwt(user, account.getId(), account.getUsername());
        //构建返回对象
        AuthorizeVO vo = new AuthorizeVO();
        vo.setExpire(jwtUtil.expireDate());
        vo.setRole(account.getRole());
        vo.setToken(token);
        vo.setUsername(account.getUsername());
        //返回统一封装的响应对象
        response.getWriter().write(RestBean.success(vo).asJSONString());
    }

    /**
     * 登录失败处理方法，返回401错误及原因
     *
     * @param request   请求对象
     * @param response  响应对象
     * @param exception 登录异常信息
     * @throws IOException IO异常
     */
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJSONString());
    }

    /**
     * 登录成功处理方法
     *
     * @param request        请求对象
     * @param response       响应对象
     * @param authentication 认证对象
     */
    public void onLogoutSuccess(HttpServletRequest request,
                                HttpServletResponse response,
                                Authentication authentication) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        String authorization = request.getHeader("Authorization");
        if (jwtUtil.invalidateJwt(authorization)) {
            writer.write(RestBean.success().asJSONString());
        } else {
            writer.write(RestBean.failure(400, "退出登录失败").asJSONString());
        }
    }

    /**
     * 未登录时访问受保护资源的处理，返回401
     *
     * @param request   请求对象
     * @param response  响应对象
     * @param exception 认证异常
     * @throws IOException IO异常
     */
    public void onUnauthorized(HttpServletRequest request,
                               HttpServletResponse response,
                               AuthenticationException exception) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(RestBean.unauthorized(exception.getMessage()).asJSONString());
    }

    /**
     * 登录后访问无权限资源的处理，返回403
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param e        权限异常
     * @throws IOException IO异常
     */
    public void onAccessDeny(HttpServletRequest request,
                             HttpServletResponse response,
                             AccessDeniedException e) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(RestBean.forbidden(e.getMessage()).asJSONString());
    }

}
